<!DOCTYPE html>
<html>
	<head>
		<title><?php echo lang('site_title');?></title>
		<meta name="viewport" content="initial-scale=1.0">
		<meta charset="utf-8">

		<link type="text/css" href="<?php echo base_url();?>static/css/google_map.css" />

		<!-- 自动填充是 Google Maps JavaScript API 中 Places 内容库的一项功能。可以使用自动填充让您的应用具有 Google 地图搜索字段的提前键入搜索行为。 -->
		<script async defer src="https://maps.googleapis.com/maps/api/js?key=<?php echo $google_ak;?>&libraries=places&callback=googleMap.processor"></script>

	</head>
	<body>
		<h1><?php echo $title; ?></h1>


		<!-- Autocomplete -->
		<div id="locationField">
			<!-- locate -->
			<input type="button" value="NavigatorGeolocationH5 Locate!" onclick="locationHelper.getGeoposition(positionCallback);">
			<input type="button" value="GoogleGeolocationApi Locate!" onclick="locationHelper.getGoogleGeolocation(geolocationCallback);">
			
			<!-- 
				定制自动完成功能的占位符文本

					默认情况下，由自动完成服务创建的文本字段包含标准的占位符文本。要修改该文本，请设置 input 元素中的 placeholder 属性：
					<input ... placeholder="Anything you want!" .../>

					注：默认的占位符文本会自动本地化。如果您指定自己的占位符值，则必须在应用中对该值进行本地化处理。
			 -->
			<input id="autocomplete" type="text" placeholder="Enter your address" style="width: 80%" />
		</div>

		<table id="address">
			<tr>
				<td class="label">Street address</td>
				<td class="slimField">
					<input class="field" id="street_number" disabled="true"></input>
				</td>
				<td class="wideField" colspan="2">
					<input class="field" id="route" disabled="true"></input>
				</td>
			</tr>
			<tr>
				<td class="label">City</td>
				<!-- Note: Selection of address components in this example is typical.
					 You may need to adjust it for the locations relevant to your app. See
					 https://developers.google.com/maps/documentation/javascript/examples/places-autocomplete-addressform
				-->
				<td class="wideField" colspan="3">
					<input class="field" id="locality" disabled="true"></input>
				</td>
			</tr>
			<tr>
				<td class="label">State</td>
				<td class="slimField">
					<input class="field" id="administrative_area_level_1" disabled="true"></input>
				</td>
				<td class="label">Zip code</td>
				<td class="wideField">
					<input class="field" id="postal_code" disabled="true"></input>
				</td>
			</tr>
			<tr>
				<td class="label">Country</td>
				<td class="wideField" colspan="3">
					<input class="field" id="country" disabled="true"></input>
				</td>
			</tr>
		</table>

		<!-- AutocompleteService -->
		<div id="locationField">
			<select id="countrySelected" name="country">
				<option value="all">All</option>
				<?php 
				foreach ($countryMap as $code => $name) {
					printf("<option value=\"%s\">%s</option>", $code, $name);
				}
				?>
			</select>
			<select id="citySelected" name="city">
				<option value="Paris">Paris</option>
				<option value="Roma">Roma</option>
			</select>
			<input id="addressInput" type="text" placeholder="Enter your address" style="width: 80%"/>
			<input id="submitBtn" type="button" value="Go!"/>
		</div>

		<!-- SearchBox -->
		<div id="locationField">
			<!-- <select id="countrySelected" name="country"></select> -->
			<!-- <select id="citySelected" name="city"></select> -->
			<input id="searchBox" type="text" placeholder="Search Box: Enter your address" style="width: 80%"/>
			<!-- <input id="submitBtn" type="button" value="Go!"/> -->
		</div>

		<div class="mapContainer" style="height:400px;"></div>

		<p>Query suggestions for &lt;Your address&gt;:</p>
		<ul id="suggestionsResults"></ul>

		<script type="text/javascript">

			var googleMap = {};
			googleMap.mapHandle = null;
			googleMap.container = document.getElementsByClassName("mapContainer")[0];
			googleMap.points = {
				init: {lat: -34.397, lng: 150.644},
				// initAutocomplete: 
				initSearchBox: {lat: -33.8688, lng: 151.2195},
			};
			googleMap.options = {
				init: {
					center: googleMap.points.init,
					zoom: 6
				},
				
				initSearchBox: {
					center: googleMap.points.initSearchBox,
					zoom: 13,
					mapTypeId: "roadmap"
				},

			};

			googleMap.processor = function() {
				this.init();
				this.initAutocomplete();
				this.initAutocompleteService();
				
				this.initSearchBox();

			};

			googleMap.init = function() {
				this.mapHandle = new google.maps.Map(this.container, this.options.init);

				return this;
			};

			/*
				地址和搜索词语的自动完成功能
				https://developers.google.com/maps/documentation/javascript/places-autocomplete

				该 API 提供了两种类型的自动完成小工具，您可以分别通过 Autocomplete 和 SearchBox 类来添加。此外，您还可以使用 AutocompleteService 类以编程方式检索自动完成的结果。

以下是可用类的摘要：

1.Autocomplete 会向网页添加一个文本输入字段，并监控该字段是否有字符输入。当用户输入文本时，自动完成功能会以下拉选取列表的形式返回预测地点。当用户从该列表中选择一个地点时，该地点的相关信息将返回给 autocomplete 对象，并且可以通过您的应用进行检索。请参阅以下详情。
2.SearchBox 也会向网页添加一个文本输入框，其方式与 Autocomplete 大致相同。区别如下：
主要区别在于选取列表中所显示的结果。SearchBox 提供了一个扩展的预测项列表，其中可以包括地点（在 Google Places API 中定义）以及建议的搜索词语。例如，如果用户输入“pizza in new”，选取列表可能会包括短语“pizza in New York, NY”以及各种披萨折扣店的名称。
在限定搜索范围方面，SearchBox 提供的选项比 Autocomplete 要少。在前者中，您可以使搜索偏向于某个给定的 LatLngBounds。在后者中，您可以将搜索限于某个特定的国家/地区和特定的地点类型，以及设置边界。
请参阅以下详情。
3.您可以创建一个 AutocompleteService 对象，从而以编程方式检索预测项。调用 getPlacePredictions() 来检索要匹配的地点，或者调用 getQueryPredictions() 来检索要匹配的地点以及建议的搜索词语。注：AutocompleteService 不会添加任何 UI 控件。上述方法将返回一个预测对象的数组。每个预测对象均包含预测的文本，以及参考信息和结果如何匹配用户输入的内容的详情。请参阅以下详情。
			 */
			
			googleMap.initAutocomplete = function() {
				var placeSearch, autocomplete;
				this.placeSearch;
				this.autocomplete;

				// Create the autocomplete object, restricting the search to geographical
				// location types.
				this.autocomplete = new google.maps.places.Autocomplete(
					/** @type {!HTMLInputElement} */(document.getElementById("autocomplete")), 
					{types: ["geocode"]}
				);

				// this.autocomplete.setComponentRestrictions({locality: "Shanghai"});

				// When the user selects an address from the dropdown, populate the address
				// fields in the form.
				this.autocomplete.addListener("place_changed", this.fillInAddress);

			};

			googleMap.fillInAddress = function() {
				// Get the place details from the autocomplete object.
				// var place = this.autocomplete.getPlace();
				var place = googleMap.autocomplete.getPlace();

				var componentForm = {
					street_number: 'short_name',
					route: 'long_name',
					locality: 'long_name',
					administrative_area_level_1: 'short_name',
					country: 'long_name',
					postal_code: 'short_name'
				  };
				for (var component in componentForm) {
					document.getElementById(component).value = '';
					document.getElementById(component).disabled = false;
				}

				console.log(place);
				// Get each component of the address from the place details
				// and fill the corresponding field on the form.
				for (var i = 0; i < place.address_components.length; i++) {
				  var addressType = place.address_components[i].types[0];
				  if (componentForm[addressType]) {
					var val = place.address_components[i][componentForm[addressType]];
					document.getElementById(addressType).value = val;
				  }

				  console.log("%s:%s", place.address_components[i].types[0],place.address_components[i]['long_name']);
				}
			};

			var positionCallback = function(position) {
				var geolocation = {
					lat: position.coords.latitude,
					lng: position.coords.longitude
				};
				var circle = new google.maps.Circle({
					center: geolocation,
					radius: position.coords.accuracy
				});
				console.log(position, circle.getBounds());

				// googleMap.mapHandle.fitBounds(circle.getBounds());
				// googleMap.mapHandle.setCenter(geolocation);
				googleMap.mapHandle.panTo(geolocation);
				// googleMap.mapHandle.panToBounds(circle.getBounds());


				// 也可以 在创建 Autocomplete 对象时设置边界 - 使用 bounds 和 types 选项来请求类型为“establishment”（场所）的商家，且偏向于指定地理区域内的那些商家。
				// 更改某个现有 Autocomplete 的边界 - 调用 setBounds() 来更改某个现有 Autocomplete 的搜索区域。
				googleMap.autocomplete.setBounds(circle.getBounds());
				// 将边界设置为地图的视口 - 使用 bindTo() 可以使结果偏向于地图的视口，即使当该视口发生变化时也能够这样。 - autocomplete.bindTo('bounds', map);
				// 将搜索限制在某个特定的国家/地区内 - 使用 componentRestrictions 选项将自动完成搜索限制在某个特定的国家/地区内。
			};

			// Bias the autocomplete object to the user's geographical location,
			// as supplied by the browser's 'navigator.geolocation' object.
			var locationHelper = {};
			locationHelper.getGeoposition = function(positionCallback, errorCallback) {
				if (typeof positionCallback !== "function") {
					positionCallback = function(position) { console.log(position); };
				}
				if (typeof errorCallback !== "function") {
					// Network location provider at 'https://www.googleapis.com/' : Returned error code 403
					errorCallback = function(error) { console.error(error); };
				}

				if (navigator.geolocation) {
					navigator.geolocation.getCurrentPosition(positionCallback, errorCallback);
				} else {
					console.error("get Geoposition failed: navigator.geolocation is undefined");
				}

				return this;
			};

			locationHelper.getGoogleGeolocation = function geolocate(geolocationCallback, errorCallback) {
				if (typeof geolocationCallback !== "function") {
					geolocationCallback = function(position) { console.log(position); };
				}
				if (typeof errorCallback !== "function") {
					errorCallback = function(error) { console.error(error); };
				}

				var apiURL = "https://www.googleapis.com/geolocation/v1/geolocate?key=AIzaSyC41CaL0BXNtMS4p0jVleFKIW4eAg4ToTw";
				var request = {};
				// 这个定义必须放到 if 外面，而且必须有 var 关键字声明 —— 新版 JS(HTML5) 作用域 BUG 修复了！？？
				// $ =  $ || {};
				var $ =  $ || {};
				// Uncaught ReferenceError: $ is not defined
				// if ($ && typeof $.ajax !== "function") {
				if (typeof $ !== "undefined" && typeof $.ajax !== "function") {
					// Uncaught ReferenceError: $$$ is not defined
					// $ = $ || {};
					// $ = typeof $ !== "undefined" && $ || {};
					$.ajax = function ajax(options) {
						options = options || {};
						// options.type = (String(options.type) || "GET").toUpperCase(); // "undefined" || "GET" === "undefined"!
						options.type = String(options.type || "GET").toUpperCase();
						options.dataType = String(options.dataType || "json").toLowerCase();
						var params = formatParams(options.data);//参数格式化

						// Step 1: 兼容性创建对象
						if (window.XMLHttpRequest) {
							// var xmlHttpRequest
							var xhr = new XMLHttpRequest();
						} else {
							var xhr = new ActiveXObjet("Microsoft.XMLHTTP");
						}

						// Step 4: 接收
						xhr.onreadystatechange = function() {
							if (xhr.readyState == 4) {
								if (xhr.status >= 200 && xhr.status < 300) {
									var response = formatResponse(xhr, options);
									console.log(xhr, xhr.response, response);
									options.success && options.success(response);
								} else {
									options.error && options.error(xhr.status);
								}
							}
						};

						// Step 2 & Step 3: 连接 和 发送
						if (options.type === "GET") {
							xhr.open("GET", options.url + "?" + params, true);
							xhr.send(null);
						}
						else if (options.type === "POST") {
							xhr.open("POST", options.url, true);

							switch (options.dataType) {
								case "json":
									xhr.setRequestHeader("Content-Type", "application/json");
									xhr.send(JSON.stringify(options.data));
								break;
								case "xml":
								//...
								default:
									//设置请求头，以表单形式提交数据
									xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
									xhr.send(params);
							}
							
							
						}
					};

					// 辅助函数，格式化参数
					function formatParams(data) {
						var arr = [];
						for(var name in data) {
							arr.push(encodeURIComponent(name) + "=" + encodeURIComponent(data[name]));
						}
						//设置随机数，防止缓存
						arr.push("t=" + Math.random());
						return arr.join("&");
					}

					// 辅助函数，格式化响应
					function formatResponse(xhr, options) {
						var response = null;
						if (options.dataType === "json") {
							try {
								response = JSON.parse(xhr.response);
							} catch (e) {
								console.error(e);
								console.log(e.toString());
								options.error && options.error(400, e.toString());
							}
						}
						else if (options.dataType === "xml") {
							response = xhr.responseText;
						}
						//...
						
						return response;
					}

					// 以上代码用原生JS定义函数
					console.log("定义 $.ajax(options) 函数！");
				}

				$.ajax({
					url: apiURL,
					type: "POST",
					data: request,
					dataType: "json",
					success: function(response) {
						// console.log(response);
						geolocationCallback(response);
					},
					error: function(status, statusText) {
						// console.log(status, statusText);
						errorCallback(statusText);
					},
				});

			};
			var geolocationCallback = function(geolocationResponse) {
				var geolocation = geolocationResponse.location;
				var circle = new google.maps.Circle({
					center: geolocation,
					radius: geolocationResponse.accuracy
				});
				console.log(geolocationResponse, circle.getBounds());
				
				googleMap.mapHandle.panTo(geolocation);

				googleMap.autocomplete.setBounds(circle.getBounds());
			};


			googleMap.initAutocompleteService = function() {
				this.autocompleteService = null;
				this.autocompleteService = new google.maps.places.AutocompleteService();

				if ("oninput" in document.getElementById("addressInput")) {
					document.getElementById("addressInput").addEventListener("input", function(event){
						// console.log(this.value === event.target.value);
						var thisValue = event.target.value;
						
						var country = document.getElementById("countrySelected").value;
						country = country === "all" ? "" : country;
						// var locality = document.getElementById("citySelected").value;
						// -> bounds;

						var autocompleteRequest = {
							input: thisValue,
							// types 参数 "(string)" 形式的和其它任何元素互斥！
							// types: ["establishment", geocode", "(regions)", "(cities)"], // INVALID_REQUEST
							// types: ["establishment", "geocode"], // OK
							// types: ["establishment", "geocode", "(Beijing)"], // INVALID_REQUEST
							types: ["(cities)"], // OK!!!
							// types: ["(regions)", "(cities)"], // INVALID_REQUEST!!!
							/*
								ComponentRestrictions:
								Type:  string
									Restricts predictions to the specified country (ISO 3166-1 Alpha-2 country code, case insensitive). E.g., us, br, au.
							 */
							componentRestrictions: {country: country},
						};

						var displaySuggestions = function(autocompletePrediction, placesServiceStatus) {
							if (placesServiceStatus != google.maps.places.PlacesServiceStatus.OK) {
								// alert(placesServiceStatus);
								// console.log(placesServiceStatus);
								console.error(placesServiceStatus);
								// console.warn(placesServiceStatus);
								return;
							}

							document.getElementById("suggestionsResults").innerHTML = "";
							autocompletePrediction.forEach(function(prediction){
								var li = document.createElement("li");
								// var parent = document.querySelector(".parent");
								// var childs = parent.querySelector("li");
								li.appendChild(document.createTextNode(prediction.description));
								// document.getElementById("suggestionsResults").removeChild().appendChild(li);
								document.getElementById("suggestionsResults").appendChild(li);
							});
						};

						googleMap.autocompleteService.getPlacePredictions(autocompleteRequest, displaySuggestions);
						// googleMap.autocompleteService.getQueryPredictions(autocompleteRequest, displaySuggestions);
					});
				}
				
			};


			// This example adds a search box to a map, using the Google Place Autocomplete
			// feature. People can enter geographical searches. The search box will return a
			// pick list containing a mix of places and predicted search terms.
			googleMap.initSearchBox = function() {
				// Create the search box and link it to the UI element.
				var input = document.getElementById("searchBox");
				var searchBox = new google.maps.places.SearchBox(input);
				// this.mapHandle.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
				
				// Bias the SearchBox results towards current map's viewport.
				this.mapHandle.addListener("bounds_changed", function() {
					searchBox.setBounds(googleMap.mapHandle.getBounds());
				});

				var markers = [];
				// Listen for the event fired when the user selects a prediction and retrieve
				// more details for that place.
				searchBox.addListener("places_changed", function() {
					var places = searchBox.getPlaces();

					if (places.length == 0) {
						return;
					}

					// Clear out the old markers.
					markers.forEach(function(marker) {
						marker.setMap(null);
					});
					markers = [];

					// For each place, get the icon, name and location.
					var bounds = new google.maps.LatLngBounds();
					places.forEach(function(place) {
						if (!place.geometry) {
							console.log("Returned place contains no geometry");
							return;
						}
						var icon = {
							url: place.icon,
							size: new google.maps.Size(71, 71),
							origin: new google.maps.Point(0, 0),
							anchor: new google.maps.Point(17, 34),
							scaledSize: new google.maps.Size(25, 25)
						};

						// Create a marker for each place.
						markers.push(new google.maps.Marker({
							map: googleMap.mapHandle,
							icon: icon,
							title: place.name,
							position: place.geometry.location
						}));

						if (place.geometry.viewport) {
							// Only geocodes have viewport.
							bounds.union(place.geometry.viewport);
						} else {
							bounds.extend(place.geometry.location);
						}
					});
					googleMap.mapHandle.fitBounds(bounds);
				});



			};










		</script>






	</body>
</html>
